-----------------------------------------------------
---- SETCLASS CLONES THE BASIC OBJECT CLASS TO CREATE NEW CLASSES
-----------------------------------------------------
-- Supports INHERITANCE 
--
-- Sam Lie, 17 May 2004 
-- Modified Code from Christian Lindig - lindig (at) cs.uni-sb.de
---------------------------------------------------------------

-- EVERYTHING INHERITS FROM THIS BASIC OBJECT CLASS
BaseObject = {
  super   = nil,
  name    = "Object",
  new     =
    function(class)
      local obj  = {class = class}
      local meta = {
        __index = function(self,key) return class.methods[key] end 
      }            
      setmetatable(obj,meta)
      return obj
    end,
  methods = {classname = function(self) return(self.class.name) end},
  data    = {}
}

function setclass(name, super)
  if (super == nil) then
    super = BaseObject
  end

  local class = {
    super = super; 
    name  = name; 
    new   =
      function(self, ...) 
        local obj = super.new(self, "___CREATE_ONLY___");
          -- check if calling function init
          -- pass arguments into init function
        if (super.methods.init) then
          obj.init_super = super.methods.init
        end

	if (self.methods.init) then
            if (tostring(arg[1]) ~= "___CREATE_ONLY___") then
              obj.init = self.methods.init
              if obj.init then
                obj:init(unpack(arg))
              end
            end
	end

        return obj
      end,  
    methods = {}
  }
    
  -- if class slot unavailable, check super class
  -- if applied to argument, pass it to the class method new        
  setmetatable(class, {
    __index = function(self,key) return self.super[key] end,
    __call  = function(self,...) return self.new(self,unpack(arg)) end 
  })

  -- if instance method unavailable, check method slot in super class    
  setmetatable(class.methods, {
    __index = function(self,key) return class.super.methods[key] end
  })
  return class
end    

-- Print Class (Helper class)

cPrintClass=setclass("PrintClass")

function cPrintClass.methods:init(feed, extrusionmultiplier) 
	if(extrusionmultiplier==nil) then extrusionmultiplier = 1 end
	if(feed==nil) then feed = 3000 end
	self.feed = feed
	self.em=extrusionmultiplier
	self.x = 0
	self.y = 0
	self.z = 0
	self.e = 0
end

function cPrintClass.methods:_goto(x, y, z, em, feed, e) 
	if( x==nil ) then x = self.x end
	if( y==nil ) then y = self.y end
	if( z==nil ) then z = self.z end
	if( em==nil ) then em = self.em end
	if( feed==nil ) then feed = self.feed end
	if( e==nil ) then e = em*math.sqrt((x-self.x)*(x-self.x)+(y-self.y)*(y-self.y)+(z-self.z)*(z-self.z)) end
	e = e + self.e
	base:AddText(string.format("G1 X%f Y%f Z%f F%f E%f\n", x,y,z,feed, e))
	self.x = x
	self.y = y
	self.z = z
	self.e = e
end

function cPrintClass.methods:gotox(x, em, feed, e) 
	self:_goto(x, nil, nil, em, feed, e)
end

function cPrintClass.methods:gotoy(y, em, feed, e) 
	self:_goto(nil, y, nil, em, feed, e)
end

function cPrintClass.methods:gotoz(z, em, feed, e) 
	self:_goto(nil, nil, nil, em, feed, e)
	self:_goto(nil, nil, z, em, feed, e)
	self:_goto(nil, nil, nil, em, 1, e)
end

function cPrintClass.methods:gotoxy(x, y, em, feed, e) 
	self:_goto(x, y, nil, em, feed, e)
end

function cPrintClass.methods:gotoxyz(x, y, z, em, feed, e) 
	self:_goto(x, y, z, em, feed, e)
end

-- Actual script:

base:ClearGcode()

printjob = cPrintClass:new(3000,1)

z=0.0
zstep=0.4
height=58
linewidth=1.1
start_diameter = 15
end_diameter = 20

ratio = 0
anglestep = math.pi / 18

xcenter = 30
ycenter = 30

function DoGoto(axis1, axis2, direction, extrusion)
	if( direction == 1 ) then
		printjob:gotoxy(xcenter+axis1, ycenter+axis2, extrusion)
	else
		printjob:gotoxy(xcenter+axis2, ycenter+axis1, extrusion)	
	end
end

function DoSide(diameter)
	angle = 0
	while (angle < math.pi*2) do
		x=diameter*math.cos(angle+ratio*(math.pi*2))
		y=diameter*math.sin(angle+ratio*(math.pi*2))
		angle = angle + anglestep
		DoGoto(x, y, 1, 1)
	end
end

		
function DoLayer(diameter, offset, extrusion, linedist, direction)
	axis1 = diameter - offset
	
	while (axis1 > -diameter ) do
		angle = math.asin(axis1/diameter)+math.pi
		axis2 = math.cos(angle)*diameter
		DoGoto(axis1, axis2, direction, 1)
		DoGoto(axis1, -axis2, direction, extrusion)
		axis1 = axis1 - linedist

		if( axis1 < -diameter ) then break end
		
		angle = math.asin(axis1/diameter)+math.pi
		axis2 = math.cos(angle)*diameter
		DoGoto(axis1, -axis2, direction, 1)
		DoGoto(axis1, axis2, direction, extrusion)
		axis1 = axis1 - linedist
	end
end	


while(z<5) do
	printjob:gotoz(z, 0, 70)
	ratio = z/height
	diameter = ratio * (end_diameter - start_diameter) + start_diameter - 1.5 * linewidth
	DoSide(diameter)
	diameter = diameter + linewidth
	DoSide(diameter)
	z = z + zstep
end

-- first suspended layer
printjob:gotoz(z, 0, 70)
ratio = z/height
diameter = ratio * (end_diameter - start_diameter) + start_diameter - 0.5 * linewidth
DoSide(diameter)

diameter = diameter - linewidth
DoLayer(diameter, 0, 0.25, 1, 1)
z = z + zstep * 0.75

printjob:gotoz(z, 0, 70)
ratio = z/height
diameter = ratio * (end_diameter - start_diameter) + start_diameter - 1.5 * linewidth
DoLayer(diameter, 0, 0.5, 1, 2)
z = z + zstep * 0.25

printjob:gotoz(z, 0, 70)
ratio = z/height
diameter = ratio * (end_diameter - start_diameter) + start_diameter - 0.5 * linewidth
DoSide(diameter)
z = z + zstep * 0.5

printjob:gotoz(z, 0, 70)
ratio = z/height
diameter = ratio * (end_diameter - start_diameter) + start_diameter - 1.5 * linewidth
DoLayer(diameter, 0, 0.75, 1, 1)
z = z + zstep * 0.5

printjob:gotoz(z, 0, 70)
ratio = z/height
diameter = ratio * (end_diameter - start_diameter) + start_diameter - 0.5 * linewidth
DoSide(diameter)
z = z + zstep * 0.25

printjob:gotoz(z, 0, 70)
ratio = z/height
diameter = ratio * (end_diameter - start_diameter) + start_diameter - 1.5 * linewidth
DoLayer(diameter, 0, 1, 1, 2)
z = z + zstep * 0.75

printjob:gotoz(z, 0, 70)
ratio = z/height
diameter = ratio * (end_diameter - start_diameter) + start_diameter - 0.5 * linewidth
DoSide(diameter)

diameter = diameter - linewidth
DoLayer(diameter, 0.5, 1, 1, 2)
z = z + zstep

printjob:gotoz(z, 0, 70)
ratio = z/height
diameter = ratio * (end_diameter - start_diameter) + start_diameter - 0.5 * linewidth
DoSide(diameter)

diameter = diameter - linewidth
DoLayer(diameter, 0, 1, 1.5, 1)
z = z + zstep

printjob:gotoz(z, 0, 70)

